package com.dylan.shot.head.shiro;

import com.dylan.shot.head.service.MemberAuthenticateService;
import org.apache.shiro.authc.AuthenticationListener;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.servlet.Filter;
import java.util.*;

/**
 * Created by Dylan on 2017/9/12.
 */
@Configuration
public class ShiroConfiguration {

    private Map<String, String> filterChainDefinitionMap() {
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/error/**", "anon");
        filterChainDefinitionMap.put("/unauthorized", "anon");
        filterChainDefinitionMap.put("/cache", "anon");
        filterChainDefinitionMap.put("/register", "anon");
        filterChainDefinitionMap.put("/page", "anon");
        filterChainDefinitionMap.put("/login", "authc");
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/self_info", "authc");
        filterChainDefinitionMap.put("/**", "user");
        return filterChainDefinitionMap;
    }

    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(MemberAuthenticateService memberAuthenticateService) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager());
        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setSuccessUrl("/");
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
        shiroFilterFactoryBean.setFilters(filters(memberAuthenticateService));
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap());
        return shiroFilterFactoryBean;
    }

    @Bean
    public org.apache.shiro.mgt.SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setAuthenticator(authenticator());
        List<Realm> realms = new ArrayList<>();
        realms.add(usernameRealm());
        realms.add(phoneRealm());
        realms.add(emailRealm());
        securityManager.setRealms(realms);
        securityManager.setSessionManager(sessionManager());
        securityManager.setCacheManager(ehCacheManager());// 所有的sessionManager,sessionDAO,realm都会被负值这个cacheManager
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
        SimpleCookie rememberMeCookie = new SimpleCookie("AUDIT_CENTER_REMEMBER_ME");
        rememberMeCookie.setMaxAge(2592000); // 记住我cookie生效时间30天
        rememberMeManager.setCookie(rememberMeCookie);
        securityManager.setRememberMeManager(rememberMeManager);
        return securityManager;
    }

    /**
     * 过滤器
     */
    private Map<String, Filter> filters(MemberAuthenticateService memberAuthenticateService) {
        Map<String, Filter> filters = new HashMap<>();
        // 此处的过滤器不要生成Bean,不然系统不可用
        filters.put("authc", formAuthenticationFilter());
        filters.put("logout", logoutFilter());
        filters.put("user", userFilter(memberAuthenticateService));
        return filters;
    }

    /**
     * 登入时的表单过滤器
     */
    private MyFormAuthenticationFilter formAuthenticationFilter() {
        MyFormAuthenticationFilter myFormAuthenticationFilter = new MyFormAuthenticationFilter();
        myFormAuthenticationFilter.setUsernameParam("username");
        myFormAuthenticationFilter.setPasswordParam("password");
        myFormAuthenticationFilter.setRememberMeParam("rememberMe");
        myFormAuthenticationFilter.setFailureKeyAttribute("shiroLoginFailure");
        myFormAuthenticationFilter.setLoginUrl("/login");
        myFormAuthenticationFilter.setSuccessUrl("/");
        return myFormAuthenticationFilter;
    }

    /**
     * 已知用户过滤器,用于记住我获取用户信息
     */
    private MyUserFilter userFilter(MemberAuthenticateService memberAuthenticateService) {
        MyUserFilter userFilter = new MyUserFilter();
        userFilter.setMemberAuthenticateService(memberAuthenticateService);
        return userFilter;
    }

    /**
     * 登出过滤器
     */
    private MyLogoutFilter logoutFilter() {
        MyLogoutFilter logoutFilter = new MyLogoutFilter();
        logoutFilter.setRedirectUrl("/login");
        return logoutFilter;
    }


    @Bean
    public ModularRealmAuthenticator authenticator() {
        ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
        // 配置认证策略，只要有一个Realm认证成功即可，并且返回第一个认证成功信息
        authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
        List<AuthenticationListener> authenticationListenerList = new ArrayList<>();
        authenticationListenerList.add(authenticationListener());
        // 监听登录和退出
        authenticator.setAuthenticationListeners(authenticationListenerList);
        return authenticator;
    }

    @Bean
    public MyAuthenticationListener authenticationListener() {
        return new MyAuthenticationListener();
    }

    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setGlobalSessionTimeout(1800000);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        EnterpriseCacheSessionDAO sessionDAO = new EnterpriseCacheSessionDAO();
        sessionDAO.setActiveSessionsCacheName("shiro-activeSessionCache");
        sessionManager.setSessionDAO(sessionDAO);
        sessionManager.setSessionIdCookieEnabled(true);
        SimpleCookie sessionIdCookie = new SimpleCookie("AUDIT_CENTER_SHIRO_ID");
        sessionIdCookie.setHttpOnly(true);
        sessionIdCookie.setMaxAge(60); // sessionId cookie的失效时间
        sessionManager.setSessionIdCookie(sessionIdCookie);
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }

    @Bean
    public EhCacheManager ehCacheManager() {
        EhCacheManager cacheManager = new EhCacheManager();
        cacheManager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
        return cacheManager;
    }

    @Bean
    public UsernameRealm usernameRealm() {
        UsernameRealm usernameRealm = new UsernameRealm();
        usernameRealm.setCredentialsMatcher(credentialsMatcher());
        usernameRealm.setCachingEnabled(true);
        usernameRealm.setAuthenticationCachingEnabled(true);
        usernameRealm.setAuthenticationCacheName("authenticationCache");
        usernameRealm.setAuthorizationCachingEnabled(true);
        usernameRealm.setAuthorizationCacheName("authorizationCache");
        return usernameRealm;
    }

    @Bean
    public PhoneRealm phoneRealm() {
        PhoneRealm phoneRealm = new PhoneRealm();
        phoneRealm.setCredentialsMatcher(credentialsMatcher());
        phoneRealm.setCredentialsMatcher(credentialsMatcher());
        phoneRealm.setCachingEnabled(true);
        phoneRealm.setAuthenticationCachingEnabled(true);
        phoneRealm.setAuthenticationCacheName("authenticationCache");
        phoneRealm.setAuthorizationCachingEnabled(true);
        phoneRealm.setAuthorizationCacheName("authorizationCache");
        return phoneRealm;
    }

    @Bean
    public EmailRealm emailRealm() {
        EmailRealm emailRealm = new EmailRealm();
        emailRealm.setCredentialsMatcher(credentialsMatcher());
        emailRealm.setCredentialsMatcher(credentialsMatcher());
        emailRealm.setCachingEnabled(true);
        emailRealm.setAuthenticationCachingEnabled(true);
        emailRealm.setAuthenticationCacheName("authenticationCache");
        emailRealm.setAuthorizationCachingEnabled(true);
        emailRealm.setAuthorizationCacheName("authorizationCache");
        return emailRealm;
    }

    @Bean
    public HashedCredentialsMatcher credentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setStoredCredentialsHexEncoded(true);
        credentialsMatcher.setHashAlgorithmName("md5");
        credentialsMatcher.setHashIterations(2);
        return credentialsMatcher;
    }

    /**
     * Shiro生命周期处理器
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(org.apache.shiro.mgt.SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}
